import pandas as pd
import numpy as np
import pickle
import dalex as dx
import warnings
warnings.filterwarnings('ignore')
Po zaimportowaniu wszystkich bibliotek wczytujemy naszą ramkę danych i wytrenowany model (MLP Regressor), a następnie wybieramy obserwację i liczymy dla niej predykcję:
housing_df = pd.read_csv('housing_preprocessed.csv')
mlp = pickle.load(open('mlp','rb'))
X = housing_df.drop('median_house_value',axis = 1)
y = housing_df.loc[:,['median_house_value']]
random_observation = X.loc[[966],:]
prediction = mlp.predict(random_observation)
print(f"Predykcja: {prediction[0]} \n Rzeczywista wartość: {y.iloc[966,0]}")
RMSE dla tego modelu wynosiło około 60 000 USD, tutaj mamy nieco ponad 75 000 USD.
Wyliczmy teraz dekompozycję predykcji modelu używając LIME:
explainer = dx.Explainer(mlp,X,y)
surrogate = explainer.predict_surrogate(random_observation,type="lime",show_all=True)
surrogate.show_in_notebook()
Dla porównania zobaczmy jak wygląda dekompozycja przy użyciu Break Down i SHAP:
explainer.predict_parts(random_observation).plot(max_vars=20)
explainer.predict_parts(random_observation,type="shap").plot(max_vars=20)
Jak widzimy wszystkie trzy dekompozycje dają w miarę zbliżone rezultaty (zarówno co do znaczenia poszczególnych zmiennych, jak i wartości dodanych/odejmowanych). Pojawiają się subtelne różnice; w powyższym przykładzie zerowa wartość zmiennej NEAR_BAY ma ujemny wpływ na wartość mieszkania dla LIME'a i SHAP, w odróżnieniu od dekompozycji Break Down.
surrogate1 = explainer.predict_surrogate(X.loc[[1025],:],type="lime",show_all=True)
surrogate1.show_in_notebook()
surrogate2 = explainer.predict_surrogate(X.loc[[1410],:],type="lime",show_all=True)
surrogate2.show_in_notebook()
surrogate3 = explainer.predict_surrogate(X.loc[[1525],:],type="lime",show_all=True)
surrogate3.show_in_notebook()
surrogate4 = explainer.predict_surrogate(X.loc[[1791],:],type="lime",show_all=True)
surrogate4.show_in_notebook()
surrogate5 = explainer.predict_surrogate(X.loc[[1918],:],type="lime",show_all=True)
surrogate5.show_in_notebook()
surrogate2 = explainer.predict_surrogate(X.loc[[1989],:],type="lime",show_all=True)
surrogate2.show_in_notebook()
LIME zgrubsza potwierdza to, co zauważyliśmy przy poprzedniej pracy domowej dzięki Break Down i SHAP. Ponownie widzimy, że dużą wartość ma zmienna median_income, często niemałe znaczenie mają także zmienne położenia, choć nie zawsze.
Dużą zaletą LIME'a jest przede wszystkim czas wykonania dekompozycji - o wiele krótszy niż w przypadku Break Down i SHAP. Osobiście jednak podoba mi się on najmniej głównie ze względu na mało atrakcyjny wygląd wykresu.
Analizując chociażby zmienną NEAR OCEAN zauważamy, że jej zerowa wartość we wszystkich przypadkach ujemny wpływ do wartości mieszkania. Wartości również są zbliżone - wszystkie wahają się pomiędzy 20 000 a 25 000, co można uznać za dość dokładny wynik.
Podobnie zachowuje się zmienna NEAR_BAY, która dla trzech obserwacji przyjmuje wartość 1 (za każdym razem dodając do wartości mieszkania nieco ponad 30 000 USD), a dla pozostałych czterech 0 (odejmując od wartości mieszkania od 20 000 do 36000 USD).
Generalnie możemy więc uznać LIME za stabilny.